 1.集合之可变和不可变集合
   
   集合的主要内容：
       1)、Scala中的可变和不可变集合
       2)、集合的三大类：Seq、Set、Map
       3)、集合的常用算子
       4)、Scala与Java之间的集合转换
   根据容器中元素的组织方式和操作方式，可以分为有序和无序、可变和不可变等不同的容器类别；
   不可变集合是指集合内的元素一旦初始化完成就不可再进行更改，任何对集合的改变都将生成一个新的
集合；
   可变集合提供了改变集合内元素的方法;
   Scala同时支持可变集合和不可变集合，主要下面两个包：
     scala.collection.mutable：定义了可变集合的特质和具体实现类
     scala.collection.immutable：定义了不可变集合的特质和具体实现类
   对于几乎所有的集合类，Scala都同时提供了可变和不可变的版本。
   Scala优先采用不可变集合，不可变集合元素不可更改，可以安全的并发访问。
   Scala集合有三大类：Seq（序列）、Set（集）、Map（映射）；
   所有的集合都扩展自Iterable特质。
   
   小结：
   String属于IndexedSeq
   Queue队列和Stack堆这两个经典的数据结构属于LinearSeq
   Map体系下有一个SortedMap，说明Scala中的Map是可以支持排序的
   mutable可变集合中Seq中的Buffer下有ListBuffer，它相当于可变的List列表；
   List列表属于Seq中的LinearSeq
 
 2.Seq
   
   Seq代表按照一定顺序排列的元素序列；
   该序列是一种特别的可迭代集合，包含可重复的元素；
   元素的顺序是确定的，每个元素对应一个索引值；
   Seq提供了两个重要的子特质：
      IndexedSeq：提供了快速随机访问元素的功能，它通过索引来查找和定位的
      LinearSeq：提供了访问head、tail的功能，它是线型的，有头部和尾部的概念，通过遍历来查找。
   1).List
   List代表元素顺序固定的不可变的链表，它是Seq的子类，在Scala编程中经常使用。
   List是函数式编程语言中典型的数据结构，与数组类似，可索引、存放类型相同的元素。
   List一旦被定义，其值就不能改变。
   
   List列表有头部和尾部的概念，可以分别使用head和tail方法来获取：
     head返回的是列表第一个元素的值
     tail返回的是除第一个元素外的其它元素构成的新列表
   这体现出列表具有递归的链表结构。
   Scala定义了一个空列表对象Nil，定义为List[Nothing]
   借助 Nil 可将多个元素用操作符 :: 添加到列表头部，常用来初始化列表；
   操作符 ::: 用于拼接两个列表；
   
package lagou.cn.part09

object ListDemo {
  def main(args: Array[String]): Unit = {
    //Nil表示一个空的列表
    // ::操作符表示向集合中添加元素，它是从右往左进行运算的，所以集合对象一定要放在最右边
    val list1 = 1 :: 2 :: 3 :: 4 :: Nil
    val list2 = 5 :: 6 :: 7 :: 8 :: Nil

    //使用 :::操作符进行了拼接，不要使用::进行列表的拼接，因为这样拼接的结果不是我们想要的
    val list3 = list1 ::: list2

    println(list3.head) //返回第一个元素
    println(list3.tail) //返回除第一个元素外的其它元素构成的新列表
    println(list3.init) //返回除最后一个元素外的其它元素构成的新列表
    println(list3.last) //返回最后一个元素
  }
}
   列表递归的结构，便于编写递归的算法：

package cn.lagou.edu.scala.section3

import scala.util.Random

object test1 {
  def main(args: Array[String]): Unit = {
    val random = new Random(100)
    val lst = List.fill(100)(random.nextInt(200))
    println(lst)

	println(s"sum(lst) = ${sum(lst)}; sum(lst) = ${lst.sum}")

	println(quickSort(lst))
  }

  // 快排
  def quickSort(lst: List[Int]): List[Int] = {
    lst match {
      case Nil => Nil
      case head :: tail =>
	    //通过partition将tail分为两部分
        //小于head的元素放入less列表中，大于head的元素放入greater列表中
        val (less, greater) = tail.partition(_ < head)
        quickSort(less) ::: head :: quickSort(greater)
    }
  }
}
   2).Queue
   队列Queue是一个先进先出的结构。
   队列是一个有序列表，在底层可以用数组或链表来实现。
   先进先出的原则，就是先存入的数据，要先取出，后存入的数据后取出。
   在Scala中，有scala.collection.mutable.Queue和scala.collection.immutable.Queue，一般来说，我
们使用的是scala.collection.mutable.Queue

package lagou.cn.part09

import scala.collection.mutable

object QueueDemo {
  def main(args: Array[String]): Unit = {
    //创建一个可变的队列
    val queue1 = new mutable.Queue[Int]()
    println(queue1)

    //队列当中添加元素
    queue1 += 1
    //队列中添加list列表
    queue1 ++= List(2, 3, 4)
    println(queue1)

    //按照进入队列的顺序，删除队列当中的元素
    //返回队列中的第一个元素，并从队列中删除这个元素
    val dequeue = queue1.dequeue()
    println(dequeue)
    println(queue1)

    //向队列中添加元素
    queue1.enqueue(5, 6, 7)
    println(queue1)

    //获取第一个和最后一个元素
    println(queue1.head)
    println(queue1.last)
  }
}


 3.Set
   
   Set(集合)是没有重复元素的对象集合，Set中的元素是唯一的；
   Set分为可变的和不可变的集合；
   默认情况下，使用的是不可变集合(引用 scala.collection.immutable.Set)；
   使用可变集合，需要引用 scala.collection.mutable.Set 包；
   
object SetDemo {
  def main(args: Array[String]): Unit = {
    // 判断元素是否存在
    val set = Set(1, 2, 3, 4, 5, 6, 7)
    println(set.exists(_ % 2 == 0))
    // 删除元素
    set.drop(1)
	// 引入可变的Set
    import scala.collection.mutable.Set
    val mutableSet = Set(4, 5, 6)

	// 增加元素、删除元素；执行成功返回true，否则返回false
    mutableSet.add(7)
    println(mutableSet)
    mutableSet.remove(7)
    println(mutableSet)

	// 使用 += / -= 增加、删除元素，表达更简洁
    mutableSet += 5
    mutableSet -= 2

	// 集合典型操作交、并、差
    //交集（&、intersect）
    println(Set(1, 2, 3) & Set(2, 3, 4))
    println(Set(1, 2, 3).intersect(Set(2, 3, 4)))
    println(Set(1, 2, 3) intersect (Set(2, 3, 4)))

	//并集(++、|、union)
    println(Set(1, 2, 3) ++ Set(2, 3, 4))
    println(Set(1, 2, 3) | Set(2, 3, 4))
    println(Set(1, 2, 3).union(Set(2, 3, 4)))

	//差集(--、&~、diff)
    //返回:包含本集合中不包含在给定集合中的元素的集合
    println(Set(1, 2, 3) -- Set(2, 3, 4))
    println(Set(1, 2, 3) &~ Set(2, 3, 4))
    println(Set(1, 2, 3).diff(Set(2, 3, 4)))
  }
}

 4.Map
   
   Map(映射)是一系列键值对的容器；Scala 提供了可变的和不可变的两种版本的Map，
   分别定义在包 scala.collection.mutable 和 scala.collection.immutable 里；
   默认情况下，Scala中使用不可变的 Map；
   如果要使用可变Map，必须导入scala.collection.mutable.Map；
   在Map中，键的值是唯一的，可以根据键来对值进行快速的检索。
   
package lagou.cn.part09

import scala.collection.mutable

object MapDemo {
  def main(args: Array[String]): Unit = {
    //使用两种方式定义Map
    val map1 = Map("a" -> 1, "b" -> 2)
    val map2 = Map(("a", 1), ("b", 2))

    map1.keys.foreach(println(_))
    map2.values.foreach(println(_))

    //访问不存在的key时，会抛出异常。Java.util.NoSuchElementException: key not found: x
    //println(map1("c"))

    //也可以使用get方法，来获取与key值相对应的value值
    //get方法会返回一个Option对象，要么要是Some(有值)，要么是None(无值)
    val num: Option[Int] = map1.get("a")
    num match {
      case None => println("None")
      case Some(x) => println(x)
    }

    //获取key值所对应的value值，如果键key不存在，那么就返回指定的默认值
    val num2: Int = map1.getOrElse("c", 0)
    println(num2)

    //创建一个可变的Map
    val map3 = scala.collection.mutable.Map("a" -> 1, "b" -> 2)
    println(map3)
    map3("a") = 10
    println(map3)

    //增加一个元素
    map3("c") = 3
    println(map3)

    //通过+=添加元素，-=删除元素
    map3 += ("d" -> 4, "f" -> 5)
    println(map3)
    map3 -= "d"
    println(map3)

    //将key与value的值互换
    val kv: mutable.Map[Int, String] = for ((k, v) <- map3) yield (v, k)
    println(kv)

    //推荐使用下面的方式将key与value的值互换
    map3.map(x => (x._2, x._1)).foreach(println(_))

    //通过拉链操作创建Map
    val a = Array(1, 2, 3)
    val b = Array("a", "b", "c")
    val c: Array[(Int, String)] = a.zip(b)
    val d: Map[Int, String] = a.zip(b).toMap
    println(d)

  }

}



 5.集合常用算子
   
   1).map、foreach & mapValues
   集合对象都有 foreach、map 算子。
   两个算子的共同点在于：都是用于遍历集合对象，并对每一项执行指定的方法；
   两个算子的差异点在于：
   foreach无返回值（准确说返回void），用于遍历集合
   map返回集合对象，用于将一个集合转换成另一个集合
   
// 使用 foreach 打印集合元素
val numlist = (1 to 10).toList
numlist.foreach(elem=>print(elem+" "))
numlist.foreach(print _)
numlist.foreach(print)

// 使用 map 对集合进行转换
numlist.map(_ > 2)
numlist.map(_ * 2)
   操作 Map集合时，mapValues用于遍历value，是map操作的一种简化形式；
   
// Range(20, 0, -2)用给定的步长值设定一个范围，从开始到结束(不包含)。
//Map(20 -> 0,18 -> 1,16 -> 2,14 -> 3,12 -> 4,10 -> 5,8 -> 6,6 -> 7,4 -> 8,2 ->9)
val map = Range(20, 0, -2).zipWithIndex.toMap

// 将map集合中的value值+100
map.map(elem => (elem._1, elem._2 + 100))
map.map{case (k,v) => (k, v+100)}
// mapValues的表达最简洁
map.mapValues(_+100)
   2).flatten & flatMap
   flatten的作用是把嵌套的结构展开，把结果放到一个集合中；
   在 flatMap 中传入一个函数，该函数对每个输入都返回一个集合（而不是一个元素），最后把生成的多
个集合“拍扁”成为一个集合；

scala> val lst1 = List(List(1,2), List(3,4))
lst1: List[List[Int]] = List(List(1, 2), List(3, 4))

scala> lst1.flatten
res5: List[Int] = List(1, 2, 3, 4)

// flatten 把一个字符串的集合展开为一个字符集合，因为字符串本身就是字符的集合
scala> val lst4 = List("Java", "hadoop")
lst4: List[String] = List(Java, hadoop)

scala> lst4.flatten
res8: List[Char] = List(J, a, v, a, h, a, d, o, o, p)
// flatten 有效的处理 Some 和 None 组成的集合。它可以展开Some元素形成一个新的集合，同时去掉
//None元素
scala> val x = Array(Some(1), None, Some(3), None)
x: Array[Option[Int]] = Array(Some(1), None, Some(3), None)

// 方法很多，flatten最简单
scala> x.flatten
res9: Array[Int] = Array(1, 3)

scala> x.collect{case Some(i) => i}
res10: Array[Int] = Array(1, 3)

scala> x.filter(!_.isEmpty).map(_.get)
res11: Array[Int] = Array(1, 3)

// 下面两条语句等价
val lst = List(List(1,2,5,6),List(3,4))

// 将 lst 中每个元素乘2，最后作为一个集合返回
// 此时 flatMap = flatten + map
//List(1,2,5,6,3,4)
lst.flatten.map(_*2)
lst.flatMap((x: List[Int]) => x.map(_*2))
lst.flatMap(_.map(_*2))

// 将字符串数组按空格切分，转换为单词数组
val lines = Array("Apache Spark has an advanced DAG execution engine",
"Spark offers over 80 high-level operators")
// 下面两条语句效果等价
//map算子产生的结果：Array(Array(Apache, Spark, has, an, advanced, DAG, execution,
engine), Array(Spark, offers, over, 80, high-level, operators))
// flatten算子产生的结果：Array(Apache, Spark, has, an, advanced, DAG, execution,
// engine, Spark, offers, over, 80, high-level, operators)
lines.map(_.split(" ")).flatten
// 此时 flatMap = map + flatten
lines.flatMap(_.split(" "))
   备注：flatMap = flatten + map 或 flatMap = map + flatten
   3).collect
   collect通过执行一个并行计算（偏函数），得到一个新的数组对象

object CollectDemo {
  //通过下面的偏函数，把chars数组的小写a转换为大写的A
  val fun: PartialFunction[Char, Char] = {
    case 'a' => 'A'
    case x => x
  }
  
  def main(args: Array[String]): Unit = {
    val chars = Array('a', 'b', 'c')
    val newchars = chars.collect(fun)
    println("newchars:" + newchars.mkString(","))
  }
}
   4).reduce
   reduce可以对集合当中的元素进行归约操作；
   还有 reduceLeft 和 reduceRight ，reduceLeft 从左向右归约，reduceRight 从右向左归约；
   
val lst1 = (1 to 10).toList
lst1.reduce(_+_)

// 为什么这里能出现两个占位符？
lst1.reduce(_+_)

// 我们说过一个占位符代表一个参数，那么两个占位符就代表两个参数。根据这个思路改写等价的语句
// x类似于buffer，缓存每次操作的数据；y每次操作传递新的集合元素
lst1.reduce((x, y) => x + y)

// 利用reduce操作，查找 lst1 中的最大值
lst1.reduce((x,y) => if (x>y) x else y)
	
// reduceLeft、reduceRight
lst1.reduceLeft((x,y) => if (x>y) x else y)
lst1.reduceRight((x,y) => if (x>y) x else y)
   5).sorted sortwith & sortby
   Scala中对于集合的排序有三种方法：sorted、sortBy、sortWith

object SortDemo {
  def main(args: Array[String]): Unit = {
    val list = List(1, 9, 3, 8, 5, 6)
    //sorted方法对一个集合进行自然排序
    //sorted源码：def sorted[B >: A](implicit ord: Ordering[B]): Repr
    //源码中有两点值得注意的地方：
    // 1.sorted方法中有个隐式参数ord: Ordering。
    // 2.sorted方法真正排序的逻辑是调用的java.util.Arrays.sort
    val numSort: List[Int] = list.sorted
    println(numSort)
    //sortBy源码：def sortBy[B](f: A => B)(implicit ord: Ordering[B]): Repr =
    //sorted(ord on f)
    //sortBy最后调用的sorted方法
    println(list.sortBy(x => x).reverse)

	//sortWith源码：def sortWith(lt: (A, A) => Boolean): Repr = sorted(Ordering
    //fromLessThan lt)
    print(list.sortWith(_ > _))
  }
}

 6.与Java集合的转换
   
   使用 scala.collection.JavaConverters 与Java集合交互。它有一系列的隐式转换，添加了asJava和
asScala的转换方法。

import scala.collection.JavaConverters._

val list: Java.util.List[Int] = List(1,2,3,4).asJava
val buffer: scala.collection.mutable.Buffer[Int] = list.asScala